05. Exercise: Create Custom View

21 4 AAK Create Custom View SC-Slide Part 1

Android Developer Documentation

Exercise

In this exercise you are going to create a custom view.

  1. Create a Kotlin app called CustomFanController using the Empty Activity template. Make sure the package name is com.example.android.customfancontroller.

  2. If there is not one, create a dimens.xml file in the values folder.

  3. Open dimens.xml and add the following dimensions:

<resources>
   <dimen name="text_view_padding">16dp</dimen>
   <dimen name="default_margin">8dp</dimen>
   <dimen name="margin_top">24dp</dimen>
   <dimen name="fan_dimen">250dp</dimen>
</resources>
  1. Open strings.xml and add the following resources. Notice that you have a title for the fan, and settings for the dial.
<resources>
   <string name="app_name">CustomFanController</string>
   <string name="fan_control">Fan Control</string>
   <string name="fan_off">off</string>
   <string name="fan_low">1</string>
   <string name="fan_medium">2</string>
   <string name="fan_high">3</string>
</resources>
  1. Open activity_main.xml in the Text tab to edit the XML code.

  2. Replace the existing TextView with this code. This text acts as a label in the activity for the custom view. You'll extract dimensions and strings later in this step.

<TextView
       android:id="@+id/customViewLabel"
       android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:padding="@dimen/text_view_padding"
       android:textColor="@android:color/black"
       android:layout_marginStart="@dimen/default_margin"
       android:layout_marginEnd="@dimen/default_margin"
       android:layout_marginTop="@dimen/margin_top"
       android:text="@string/fan_control"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"/>
  1. Add this ImageView element to the layout. This is a placeholder for the custom view you will create in this lesson.
<ImageView
       android:id="@+id/dialView"
       android:layout_width="200dp"
       android:layout_height="200dp"
       android:background="@android:color/darker_gray"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="@dimen/default_margin"
       android:layout_marginRight="@dimen/default_margin"
       android:layout_marginTop="@dimen/default_margin"/>
  1. Click the Design tab. The layout should look like this:

  1. Create a new Kotlin class called DialView.

  2. Modify the class definition to extend View. Import android.view.View when prompted.

  3. Click on View and then click the red bulb. Choose Add Android View constructors using '@JvmOverloads'. Android Studio adds the constructor from the View class. The @JvmOverloads annotation instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values.

class DialView @JvmOverloads constructor(
   context: Context,
   attrs: AttributeSet? = null,
   defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}
  1. Above the DialView class definition, just below the imports, add a top-level enum to represent the available fan speeds. Note that this enum is of type Int because the values are string resources rather than actual strings.
private enum class FanSpeed(val label: Int) {
   OFF(R.string.fan_off),
   LOW(R.string.fan_low),
   MEDIUM(R.string.fan_medium),
   HIGH(R.string.fan_high);
}
  1. Below the enum, add these constants. You'll use these as part of drawing the dial indicators and labels.
private const val RADIUS_OFFSET_LABEL = 30      
private const val RADIUS_OFFSET_INDICATOR = -35
  1. Inside the DialView class, define several variables you need in order to draw the custom view. Import android.graphics.PointF if requested.
private var radius = 0.0f                   // Radius of the circle.
private var fanSpeed = FanSpeed.OFF         // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)
  • The radius is the current radius of the circle. This value is set when the view is drawn on the screen.

  • The fanSpeed is the current speed of the fan, which is one of the values in the FanSpeed enumeration. By default that value is OFF.

  • Finally pointPosition is an X,Y point that will be used for drawing several of the view's elements on the screen.

These values are created and initialized here instead of when the view is actually drawn to ensure that the actual drawing step runs as fast as possible.

  1. Inside the DialView class definition, initialize a Paint object with a handful of basic styles. Import android.graphics.Paint and android.graphics.Typeface when requested. As with the variables, these styles are initialized here to help speed up the drawing step.
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
   style = Paint.Style.FILL
   textAlign = Paint.Align.CENTER
   textSize = 55.0f
   typeface = Typeface.create( "", Typeface.BOLD)
}